## Threading ######################################

import sys
import traceback
import threading
import Queue
import utility

class Handler(object):
    '''Base handler object'''
    
    def __init__(self, my_q):
        super(Handler, self).__init__()
        self.q = my_q

    def response(self, *args):
        print 'Generic response handler: %s' % repr(args)

    def error(self, *args):
        print 'Generic error handler: %s' % repr(args)

    def exception(self, *args):
        print 'Generic exception handler: %s' % repr(args)


class GameThread(threading.Thread):


    transaction_id = utility.UniqueID()

    @staticmethod
    def respond_ret(ret_q, dest_q, trans_id, fn, *args):
        dest_q.put((ret_q, trans_id, fn, args))

    @staticmethod
    def respond(dest_q, trans_id, fn, *args):
        dest_q.put((None, trans_id, fn, args))

    @staticmethod
    def ntfy(dest_q, fn, *args):
        trans_num = GameThread.transaction_id.next_id()
        GameThread.respond(dest_q, trans_num, fn, *args)

    @staticmethod
    def rpc(dest_q, fn, *args, **kwargs):
        ret_q = Queue.Queue()
        trans_num = GameThread.transaction_id.next_id()
        dest_q.put((ret_q, trans_num, fn, args))
        (q, trans_ret, msg, resp) = ret_q.get()

        if trans_ret != trans_num:
            raise Exception( '%s: Received response not corresopnding to transaction' % self.name )

        if msg == 'error':
            raise Exception('%s: Invalid function invocation: %s(*%s)' % (self.name, fn, repr(args)))
        
        return resp[ 0 ]
        
    
    def __init__(self, name, handler_factory = Handler, *handler_args):
        super(GameThread, self).__init__(name=name)
        self.in_q = Queue.Queue()
        self.handler_obj = handler_factory(self.in_q, *handler_args)

    def run(self):
        while True:
            msg = self.in_q.get(True)
            (sq, trans_id, cmd, args) = msg

#            print '%s: Message received: %s' % (self.name, repr((sq, trans_id, cmd, args)))
            
            sender_q = utility.MaybeObj(sq)  # proxy object in case no response is required
            
            if cmd == 'exit':
                print '%s: Exiting' % self.name
                break
            
            fn = getattr(self.handler_obj, cmd, None)

            if fn:
                try:
                    resp = fn(*args)
                    GameThread.respond(sender_q, trans_id, 'response', resp )
                except Exception as e:

                    tb = traceback.format_tb(sys.exc_info()[2])
                    
                    if not sender_q.valid_obj():
                        print '%s: Exception thrown handling request: %s %s %s %s' % (self.name, fn, repr(args), repr(e), repr(tb))
                    else:
                        GameThread.respond(sender_q, trans_id, 'exception', fn, args, e)
            else:
                if not sender_q.valid_obj():
                    print '%s: Requested call not found: %s %s' % (self.name, cmd, repr(args))
                else:
                    GameThread.respond(sender_q, trans_id, 'error', 'Invalid command', cmd)
                    
        print '%s: Exited while loop' % self.name


if __name__ == '__main__':
    pass
